#ifndef SOCKS5_SERVER_H
#define SOCKS5_SERVER_H
#pragma once

#define KILO_BYTE 1024

#include <KByteRate.h>
#include <KNetScene.h>
#include <KPageList.h>
#include <string>

class KIpAddress;
class KTimerQueue;
class S5ActionClient;
class S5ActionServer;

class S5Server : public KNetItem {
public:
    explicit S5Server(LONG max = 10000, u_short port = 1080);
    virtual ~S5Server();
public:
    LONG clients() const;
    LONG maxClients() const                 { return m_max; }
    LONG totalClients() const;
    LONG extraAccepts() const;
    void setExtraAccepts(LONG extra);
    bool isRunning() const;
    u_short listenPort() const              { return m_port; }
    size_t maxPoolSize() const              { return m_pool.maxSize(); }
    void setMaxPoolSize(size_t size = 0)    { m_pool.setMaxSize(size); }
    size_t poolSize() const                 { return m_pool.size(); }
    KTimerQueue *queue() const              { return m_queue; }
public:
    double kbDownload()                     { return double(m_download.totalBytes()) / KILO_BYTE; }
    double kbUpload()                       { return double(m_upload.totalBytes()) / KILO_BYTE; }
    double kbpsDownload()                   { return m_download.bytesPerSecond() / KILO_BYTE; }
    double kbpsUpload()                     { return m_upload.bytesPerSecond() / KILO_BYTE; }
    void newDownload(size_t bytes)          { m_download.accumulate(bytes); }
    void newUpload(size_t bytes)            { m_upload.accumulate(bytes); }
public:
    virtual bool allow(const KIpAddress &address) const;
    virtual bool requireAuthentication() const;
    virtual bool verify(const std::string &username, const std::string &password) const;
protected:
    bool sendAck(KSocket *sock, const void *data, size_t length);
protected:
    virtual void onAborted(S5ActionClient *client);
    virtual void onClientsChanged(LONG current) {}
    virtual void onEval(S5ActionClient *client) {}
protected:
    int filter() const
    {
        return KNetItem::FilterAccepted
            | KNetItem::FilterClosing
            | KNetItem::FilterConnected
            | KNetItem::FilterError
            | KNetItem::FilterReceived
            | KNetItem::FilterSent
            ;
    }
    KPageList *pool()
    {
        return &m_pool;
    }
protected:
    void onStarted();
    void onStopped();
private:
    S5ActionServer *m_context;
    LONG            m_extra;
    const LONG      m_max;
    const u_short   m_port;
    KByteRate       m_download;
    KByteRate       m_upload;
    KPageList       m_pool;
    KTimerQueue *   m_queue;
    friend class    S5Action;
    friend class    S5ActionBind;
    friend class    S5ActionClient;
    friend class    S5ActionConnect;
    friend class    S5ActionServer;
    friend class    S5ActionUdp;
};

#endif // SOCKS5_SERVER_H